#ifndef PROTOMSG_PROTOCOL_DESCRIPTOR_H_
#define PROTOMSG_PROTOCOL_DESCRIPTOR_H_
#include "protomsg_config.h"
#include "serialization.h"

namespace protomsg{
  class ProtocolFieldEnumeration;
  class ProtocolFieldScalar;
  class ProtocolFieldSubMessage;
  class ProtocolFieldUnion;
  class ProtocolMessageDescriptor;
  class ProtocolMessageDescriptorPool ;
  class ProtocolMessageDescriptorBuilder;
  class ProtocolMessageRefelection;
  class SerializedData;
  class DeserializedData;
  // Protocol
  // A protocol is a description of meta information for a data
  // buffer . Given a encoded data buffer with a related protocol
  // the protocol msg is able to decoded this buffer into a 
  // object , reversely , a data buffer with a protocol description
  // passing to the protocol-msg , the lib is also able to encoded
  // the buffer into the encoded buffer .Therefore , it is very 
  // natural that the protocol description is very important since
  // it tells the lib how to deal with the data including the type
  // checking stuff .
  // Protocol layout : a protocol is layout as below , a protocol is
  // beyond any language , we use a DSL to describe it .
  // Namespace[ Name:optional ]
  //   Message:repeated[ Name:required ]
  //     MessageField:repeated[ Name:required Type:required,enumeration Selection:optional,default ]
  // More information in the file: protomsg.protocol.xml
  // Here we can see the architecture of this message protocol  .
  // The namespace contains the message , the message contains the message_field
  // the field contains the Name(key) , Type( enumeration type ) and Selection .
  // apparently , a recursive one definition , the protocol can 
  // be descried by itself .
  class ProtocolFieldBase{
  public:
    const char* name() const {
      return name_->c_str();
    }
    int option() const {
      return option_;
    }
    int type() const {
      return type_;
    }
    int tag() const {
      return tag_;
    }
  private:
    template < typename T > struct get_dc_type {};
    template <> struct get_dc_type<ProtocolFieldEnumeration> {
      static int value = PROTOMSG_ENUM ;
    };
    template <> struct get_dc_type<ProtocolFieldScalar> {
      static int value = PROTOMSG_SCALAR;
    };
    template <> struct get_dc_type<ProtocolFieldSubMessage> {
      static int value = PROTOMSG_MESSAGE;
    };
  private:
    // this may not be that kind of safe , however , we have no choice 
    // but to do so .
    template< typename T > T* DownCast() {
      DASSERT( type_ <= get_dc_type<T>::value );
      return static_cast<T*>(this);
    }
  private:
    ProtocolFieldBase( FieldKey* key , FieldOption option , FieldType type , int tag ) : 
         name_(key) , option_(option) , type_(type) , tag_(tag) {}
    // the reference to the internal string name of this field
    FieldKey* name_;
    // the option of this field
    FieldOption option_;
    // the type of this field
    FieldType type_; 
    // the integer tag of this field
    int tag_;
  };

  // This class represent the enumeration type of the ProtocolField
  class ProtocolFieldEnumeration : public ProtocolFieldBase {
  private:
    // This function will be used as the Construction of this MessageFieldEnumeration 
    // this field can only construct by the MessageUnionField object , since it will
    // hide the detail type of MessageField .
    ProtocolFieldEnumeration( int option , FieldKey* feildkey , int tag ) 
      : ProtocolFieldBase( feildkey , option , PROTOMSG_ENUM , tag ) {}
    // This class will be instancing by the ProtocolFieldUnion class 
    friend class ProtocolMessageDescriptor;
  public:
    // the specific interface for the enumeration class
    // if the specified key_name exists , the function will 
    // return true and store the value into the 'value' you
    // specified , otherwise it will return false.
    Enumerator* GetEnumerator( const FieldKey& key_name );
    Enumerator* GetEnumerator( const char* key_name );
  private:
    // using descriptor to query the enumerator
    ProtocolMessageDescriptor* descriptor_;
  };

  // This class represent the scalar type of the ProtocolField
  class ProtocolFieldScalar : public ProtocolFieldBase {
  private:
    ProtocolFieldScalar( int option , FieldKey* fieldkey , int tag , int scalar_type ) 
      :ProtocolFieldBase( fieldkey , option , scalar_type , tag ) {
        // Checking the scalar type
        DASSERT( scalar_type <= PROTOMSG_SCALAR );
    }
    friend class ProtocolMessageDescriptor;
  };
  // This class represent the sub type of message in a ProtocolField
  class ProtocolFieldSubMessage : public ProtocolFieldBase {
  public:
    // The following GetXXX function is used to
    // get the specific information of a protocol
    // message , however it is all immutable function
    // which means you can not change them .
    // If the target thing is not existed
    // it will return NULL , otherwise return the pointer
    const char* ParentMessageName() const;
    const char* TopMessageName() const;

    ProtocolFieldUnion* GetMessageField( const char* name );
    ProtocolFieldUnion* GetMessageField( const FieldKey& name );

    ProtocolFieldEnumeration* GetEnumerationField( const char* name );
    ProtocolFieldEnumeration* GetEnumerationField( const FieldKey& name );

    ProtocolFieldSubMessage*  GetDirectSubMessageField( const char* name );
    ProtocolFieldSubMessage*  GetDirectSubMessageField( const FieldKey& name );

    ProtocolFieldScalar* GetScalarMessageField( const char* name );
    ProtocolFieldScalar* GetScalarMessageField( const FieldKey& name );

  private:
    ProtocolFieldScalar( int option , FieldKey* fieldkey , int tag , int scalar_type ) 
      :ProtocolFieldBase( fieldkey , option , scalar_type , tag ) {
        // Checking the scalar type
        DASSERT( scalar_type <= PROTOMSG_SCALAR );
    }
    friend class ProtocolMessageDescriptor;
  private:
    ProtocolMessageDescriptor* descriptor_;
  };
  // A protocol field union is a class to abstract the class:
  // ProtocolFieldEnumeration , ProtocolFieldSubMessage, ProtocolFieldScalar
  // Using this class can help you lazy-initialize the target-class
  // and safely downcast the target class . This is designed to help
  // SerializedData and DeserializedData to perform its internal function
  // more properly .
  class ProtocolFieldUnion {
  public:
    // This function will help you to determine which real class is this Union class 
    // represents .eg: ProtocolFieldUnion* ptr;
    // if( ptr->MatchClassType<ProtocolFieldScalar>() ) {
    //   ProtocolFieldScalar* ptr = ptr->DownCast<ProtocolFieldScalar>();
    // }
    template < typename class_type > bool MatchClassType() {
      return false;
    }
    template<> bool MatchClassType< ProtocolFieldScalar >() {
      return type_ <= PROTOMSG_SCALAR;
    }
    template<> bool MatchClassType< ProtocolFieldSubMessage >() {
      return type_ == PROTOMSG_MESSAGE;
    }
    template<> bool MatchClassType< ProtocolFieldEnumeration >() {
      return type_ == PROTOMSG_ENUM;
    }
    // This function will help you to determine which real class is this Union class 
    // represents .eg: ProtocolFieldUnion* ptr;
    // if( ptr->MatchClassType<PROTOMSG_INT>() ) {
    //   ProtocolFieldScalar* ptr = ptr->DownCast<ProtocolFieldScalar>();
    // }
    template < size_t class_type_tag> bool MatchClassType() {
      // 0 is forbidden 
      if( type_ <= PROTOMSG_SCALAR )
        return class_type_tag <= type_ && class_type_tag >0 ;
      else 
        return class_type_tag == type_;
    }
    // This function will help you safely downcast this class to a specific class type
    // this may not be that kind of safe , however , we have no choice 
    // but to do so .
    template< typename T > T* DownCast() {
      DASSERT( MatchClassType<T>() );
      return (T*)(InternalDownCast());
    }
    // Get the type of this class , the return value is the same as the
    // PROTOMSG_XXX type indicator .
    int type() const {
      return type_;
    }
    const char* name() const {
      return name_->c_str();
    }
  private:
    // This function will perform the internal initialization 
    // of the down cast type . It do return a void* pointer 
    // however ,the internal class has been done properly .
    void* InternalDownCast();
    // The default pointer of this protocol-field-union class
    // this class must be initialized by the ProtocolMessageDescriptor
    explicit ProtocolFieldUnion( int type ) : type_(type) , uptr_.enumeration(NULL) {}
    // The dtor will destruct the ProtocolFieldXXX class internally
    // if it has been constructed .
    ~ProtocolFieldUnion();
  private:
    enum {
      // the internal storage size will be the max of
      // three size which is ProtocolFieldScalar , ProtocolFieldSubMessage , 
      // ProtocolFieldEnumeration
      INTERNAL_STORAGE_SIZE = util::triple_max< 
      sizeof(ProtocolFieldScalar) , 
      sizeof(ProtocolFieldSubMessage) , 
      sizeof(ProtocolFieldEnumeration) >::value
    };
    // internal storage size goes here
    uint8_t internal_storage_[ INTERNAL_STORAGE_SIZE ];
    // internal tag for changing
    int type_;
    // a pointer for accessing all the object internally
    union {
      ProtocolFieldScalar* scalar;
      ProtocolFieldSubMessage* message;
      ProtocolFieldEnumeration* enumeration;
    } uptr_;
    // a reference to the field descriptor
    ProtocolMessageDescriptor* descriptor_;
    // a key for fetching the certain field in that message 
    FieldKey* name_;
    friend class ProtocolMessageDescriptor;
  };


  //========================================================
  // Descriptor implementation .
  // ProtocolMessageDescriptor will really store the data in its
  // memory . It can set up manually or use other class to 
  // set up . ProtocolMessageDescriptor is a representation of 
  // a protocol , and also it should be able to perform function
  // of serialize and deserialize , therefore , it must contain 
  // its own protocol representation ,which means it has a object
  // of itself .However , the construction of this descriptor should
  // be done by hand for it has no input data as well .Generally 
  // speaking , the Descriptor need a Protocol Declaration file to
  // construct itself or done the construction manually . We separate
  // the construction process into the class ProtocolBuilder .
  // In addition , we also have a descriptor pool which acts as
  // the manager to manage all the descriptor in the whole system
  // You can obtain the real ProtocolMessageDescriptor by name and
  // namespace from the ProtocolMessageDescriptorPool .
  class ProtocolMessageDescriptor : public Serialization , public Deserialization , public ProtocolFieldBase {
  public:
    // Query interface 
    //========== Enumeration interface 
    ProtocolFieldEnumeration* GetEnumeration( const char* name );
    ProtocolFieldEnumeration* GetEnumeration( FieldKey& name ) ;
    //========== Message interface
    ProtocolFieldSubMessage* GetSubMessage( const char* name );
    ProtocolFieldSubMessage* GetSubMessage( const FieldKey& name );
    //========== Scalar interface
    ProtocolFieldScalar* GetScalar( const char* name );
    ProtocolFieldScalar* GetScalar( const FieldKey& name );
    //========== Union interface 
    // Union interface is not the internally stored type , therefore 
    // the user should take responsibility to take care of the memory
    // using .It is typically very useful when you do not know the type
    // of this field , you just know its name .
    bool GetAnyField( ProtocolFieldUnion* field , const char* name );
    bool GetAnyField( ProtocolFieldUnion* field , const FieldKey& name ) {
      return GetAnyField( field , name.c_str() );
    }
  public:
    // Get the self-descriptor which is a descriptor used to
    // describe the protocol itself
    ProtocolMessageDescriptor* self_descriptor() const {
      return self_descriptor_;
    }
  private:
    //====== Serialized Deserialized Type-Check interface
    // The following interface is aimed to provide easy check for  
    // the serialized and deserialized data for checking the specific
    // type .
    // Eg: Using SerializedData class to serialized a object into the
    // should make the corresponding class --- SerializedData to do 
    // the internal checking stuff 
    // The MetadataCheckTransaction will easily help you to do the task
    // You just need this instance , and push the type into its-interface
    // it will help you to do the checking stuff .
    class MetadataCheckTransaction;
    // Create a meta-data-check transaction 
    MetadataCheckTransaction* CreateCheckingTransaction() ;
    // Destroy checking transaction .
    void DestroyCheckingTransaction( MetadataCheckTransaction* transaction );

  private:
    //======= The following stuff is help to create the target class internally 
    // In fact , the ProtocolMessageDescriptor should also take responsibility to
    // do the construction and destroy of ProtocolFieldXXX class 
    bool ConstructProtocolFieldEnumeration( void* buffer , const FieldKey& name );
    bool ConstructProtocolFieldSubMessage ( void* buffer , const FieldKey& name );
    bool ConstructProtocolFieldScalar     ( void* buffer , const FieldKey& name );
  private:
    // Namespace of this message 
    FieldKey* namespace_;
    // This descriptor is describing the protocol itself
    ProtocolMessageDescriptor* self_descriptor_;
    // We just using a nested class to avoid to many interface inside only one
    // class , the DescriptorTable will handle all the table searching stuff 
    // and hide the internal implementation detail;
    class DescriptorTable; 
    DescriptorTable* table_;
    // friend class 
    friend class ProtocolMessageDescriptorBuilder;
    friend class ProtocolMessageDescriptorPool;
    friend class ProtocolMessageRefelection;
    friend class ProtocolFieldUnion;
  };

}//namespace protomsg



#endif// 